#pragma once

#include <memory.h>

#include "command.hpp"
#include "components.hpp"

#include <entt/entt.hpp>

#include "baseCommandReceiver.hpp"

namespace qbe::undoRedoT {

template <typename T>
class DeleObjCR : public BaseCommandReceiver {
 public:
  DeleObjCR();
  ~DeleObjCR();

  virtual bool Action(bool bUndo) override;
  void Prepare(qlib::Library::s_ptr _library, entt::registry* _reg, entt::entity _entity, ecs::EventWriter<Event>& _event_writer,
               layout::LayoutCmd* _cmd, bool _use_cmd = true);

 private:
  entt::registry* reg = nullptr;
  entt::entity entity = entt::null;
  qlib::Library::s_ptr library = nullptr;

  bool use_cmd;

  std::shared_ptr<T> ptr;
  ecs::EventWriter<Event>* event_writer;
  layout::LayoutCmd* cmd;

  bool first{true};
  bool redo{false};
  bool undo{false};
};

template <typename T>
DeleObjCR<T>::DeleObjCR() {}

template <typename T>
DeleObjCR<T>::~DeleObjCR() {
  if (reg != nullptr && reg->try_get<InUndoRedoStack>(entity) != nullptr) {
    auto& in = reg->get<InUndoRedoStack>(entity);
    in.num--;

    if (in.num == 0) {
      reg->remove<InUndoRedoStack>(entity);

      if (reg->any_of<ObjDeleteTag>(entity)) {
        // reg->remove<ObjDeleteTag>(entity);
        // reg->emplace_or_replace<ObjDeleteForeverTag>(entity);
        // event_writer->send({Code::ObjDeleteForever});
      }
    }
  }
}

template <typename T>
void DeleObjCR<T>::Prepare(qlib::Library::s_ptr _library, entt::registry* _reg, entt::entity _entity, ecs::EventWriter<Event>& _event_writer,
                           layout::LayoutCmd* _cmd, bool _use_cmd) {
  library = _library;

  reg = _reg;
  entity = _entity;

  ptr = reg->get<std::shared_ptr<T>>(entity);
  redo = true;

  event_writer = &_event_writer;
  cmd = _cmd;

  use_cmd = _use_cmd;
}

template <typename T>
bool DeleObjCR<T>::Action(bool bUndo) {
  if ((bUndo && !undo) || (!bUndo && !redo))
    return false;

  if (!reg->valid(entity)) {
    return false;
  }

  // if constexpr (std::is_same_v<qlib::Reference, T>) {
  //   if (ptr->cell == nullptr) {
  //     return false;
  //   }

  //   bool have_cell = false;
  //   auto check_cell = ptr->cell;

  //   for (auto cell : library->cells) {
  //     if (cell == check_cell) {
  //       have_cell = true;
  //     }
  //   }

  //   if (!have_cell)
  //     return false;
  // }

  if (first) {
    if (reg->try_get<InUndoRedoStack>(entity) == nullptr) {
      reg->emplace<InUndoRedoStack>(entity);
    }

    auto& in = reg->get<InUndoRedoStack>(entity);
    in.num++;

    // reg->remove<std::shared_ptr<T>>(entity);

    first = false;
    // reg->emplace_or_replace<ObjDeleteTag>(entity);

    // event_writer->send({Code::ObjDelete});
    // cmd->remove<std::shared_ptr<T>>(entity);
  } else if (bUndo) {
    reg->emplace_or_replace<std::shared_ptr<T>>(entity, ptr);

    reg->emplace_or_replace<ObjAddTag>(entity);
    // event_writer->send({Code::ObjAdd});

    if (use_cmd)
      cmd->add(entity, ptr);
  } else {
    // reg->remove<std::shared_ptr<T>>(entity);

    reg->emplace_or_replace<ObjDeleteTag>(entity);
    // event_writer->send({Code::ObjDelete});

    if (use_cmd)
      cmd->remove(entity);
  }

  undo = !undo;
  redo = !redo;

  return true;
}

}  // namespace qbe::undoRedoT
